﻿using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using System;

namespace DoomWad
{
	public class WadReader
	{
		public const int wad_info_size = 12;
		public const int wad_lump_size = 16;
		public const int wad_id_size = 4;

		public const int wad_string_size = 8;

		public const int wad_mapvertex_size = 4;
		public const int wad_maplinedef_size = 14;
		public const int wad_mapsector_size = 26;
		public const int wad_mapsidedef_size = 30;
		public const int wad_mapsubsector_size = 4;
		public const int wad_mapseg_size = 12;
		public const int wad_mapnode_size = 28;

		public const int wad_flat_side_size = 64;
		public const int wad_flat_size = wad_flat_side_size * wad_flat_side_size;

		public const int wad_palette_channels = 3;
		public const int wad_palette_colors = 256;
		public const int wad_palette_count = 14;
		public const int wad_palette_size = wad_palette_channels * wad_palette_colors * wad_palette_count;

		public const int wad_colormap_levels = 34;
		public const int wad_colormap_bright_levels = 32;
		public const int wad_colormap_size = wad_palette_colors * wad_colormap_levels;

		public const string skyTexName = "F_SKY1";

		public bool makeTextureAssets = false;

		private string wadname;
		private BinaryReader reader;

		private wadinfo_t _wadinfo;
		private filelump_t[] _lumptable;
		private byte[] _palette;
		private byte[] _colormap;
		private string[] _pnames;
		private maptexture_t[] _maptextures;


		public static bool CheckWadFile( string wadfile)
		{
			WadReader wad = new WadReader( wadfile );
			bool iswad = wad.isWad;
			wad.EndReading();
			return iswad;
		}

		public WadReader( string wadfile )
		{
			// Пробуем открыть файл
			reader = BeginReading( wadfile );
			if ( reader == null )
				throw new UnityException( "Не удалось открыть файл " + wadfile );
			this.wadname = wadfile;
		}


		public bool isWad { get { return wadinfo.identification == "IWAD" || wadinfo.identification == "PWAD"; } }

		public wadinfo_t wadinfo
		{
			get
			{
				if ( _wadinfo == null )
					_wadinfo = ReadWADHeader();
				return _wadinfo;
			}
		}

		public filelump_t[] lumptable
		{
			get
			{
				if ( _lumptable == null )
					_lumptable = ReadLumpTable();
				return _lumptable;
			}
		}

		public byte[] palette
		{
			get
			{
				if ( _palette == null )
					_palette = ReadLumpData( "playpal" );
				return _palette;
			}
		}

		public byte[] colormap
		{
			get
			{
				if ( _colormap == null )
					_colormap = ReadLumpData( "colormap" );
				return _colormap;
			}
		}

		public string[] pnames
		{
			get
			{
				if ( _pnames == null )
					_pnames = ReadPatchNames( "pnames" );
				return _pnames;
			}
		}

		public maptexture_t[] maptextures
		{
			get
			{
				if ( _maptextures == null )
				{
					maptexture_t[] lmp_tex1 = ReadMapTextures( "texture1" );
					maptexture_t[] lmp_tex2 = ReadMapTextures( "texture2" );

					_maptextures = new maptexture_t[ lmp_tex1.Length + lmp_tex2.Length ];
					lmp_tex1.CopyTo( _maptextures, 0 );
					lmp_tex2.CopyTo( _maptextures, lmp_tex1.Length );
				}
				return _maptextures;
			}
		}

		public BinaryReader BeginReading( string filename )
		{
			if ( filename == "" )
				return null;

			FileStream stream = new FileStream( filename, FileMode.Open, FileAccess.Read );
			if ( !stream.CanRead )
			{
				Debug.Log( "Не удается открыть файл " + filename );
				return null;
			}

			BinaryReader reader = new BinaryReader( stream, Encoding.Default );
			return reader;
		}

		public void EndReading()
		{
			EndReading( reader );
		}

		public void EndReading( BinaryReader reader )
		{
			reader.Close();
		}

		public void EndReading( BinaryReader reader, string errorMessage )
		{
			Debug.LogError( errorMessage );
			reader.Close();
		}

		public string[] GetMapList()
		{
			List<string> map_list = new List<string>();

			foreach ( filelump_t l in lumptable )
			{
				if ( l.name.StartsWith( "MAP" ) ||
					l.name.StartsWith( "E1M" ) ||
					l.name.StartsWith( "E2M" ) ||
					l.name.StartsWith( "E3M" ) )
				{
					map_list.Add( l.name );
				}
			}

			return map_list.ToArray();
		}

		public WadMapData LoadMap( string mapname, FilterMode textureFilter )
		{
			// Выводим инфо

			Log.WriteLine( "Имя файла: " + wadname );
			Log.WriteLine( "Размер файла: " + reader.BaseStream.Length );
			Log.WriteLine( "wadinfo.identification: " + wadinfo.identification );
			Log.WriteLine( "wadinfo.numlumps: " + wadinfo.numlumps );
			Log.WriteLine( "wadinfo.infotableofs: " + wadinfo.infotableofs );

			// Читаем карту

			WadMapData map = ReadMapData( reader, mapname );
			if ( map == null )
			{
				EndReading( reader, "Не удалось считать данные карты" );
				return null;
			}

			// ЗАГРУЖАЕМ ТЕКСТУРЫ

			map.textures = new Dictionary<string, Texture>();

			// Из секторов
			for ( int i = 0; i < map.sectors.Length; i++ )
			{
				LoadUniqueTexture( map, map.sectors[ i ].floorpic, false, textureFilter );
				LoadUniqueTexture( map, map.sectors[ i ].ceilingpic, false, textureFilter );
			}

			// Из стен
			for ( int i = 0; i < map.linedefs.Length; i++ )
			{
				maplinedef_t ld = map.linedefs[ i ];

				if ( ld.rsidenum != -1 )
				{
					mapsidedef_t sd = map.sidedefs[ ld.rsidenum ];
					LoadUniqueTexture( map, sd.toptexture, true, textureFilter );
					LoadUniqueTexture( map, sd.midtexture, true, textureFilter );
					LoadUniqueTexture( map, sd.bottomtexture, true, textureFilter );
				}

				if ( ld.lsidenum != -1 )
				{
					mapsidedef_t sd = map.sidedefs[ ld.lsidenum ];
					LoadUniqueTexture( map, sd.toptexture, true, textureFilter );
					LoadUniqueTexture( map, sd.midtexture, true, textureFilter );
					LoadUniqueTexture( map, sd.bottomtexture, true, textureFilter );
				}
			}

			EndReading( reader );
			return map;
		}

		public void LoadUniqueTexture( WadMapData map, string texname, bool wall, FilterMode textureFilter )
		{
			if ( texname == "" )
				return;

			if ( texname == "-" )
				return;

			if ( map.textures.ContainsKey( texname ) )
				return;

			Texture tex = wall ?
				CreateTexture_Wall( texname, textureFilter ) :
				CreateTexture_Flat( texname, textureFilter );

			if ( !tex )
				return;

			if ( makeTextureAssets )
			{
				string path = "Assets/Textures/" + texname + ".png";
				FileUtils.SaveTextureToFile( tex, path );
				AssetDatabase.Refresh();
				tex = AssetDatabase.LoadAssetAtPath( path, typeof( Texture ) ) as Texture;
				tex.filterMode = textureFilter;
			}

			tex.name = texname + "_Tex";
			map.textures[ texname ] = tex;
		}

		public wadinfo_t ReadWADHeader()
		{
			reader.BaseStream.Seek( 0, SeekOrigin.Begin );

			wadinfo_t w = new wadinfo_t();
			w.identification = new string( reader.ReadChars( wad_id_size ) );
			w.numlumps = reader.ReadInt32();
			w.infotableofs = reader.ReadInt32();

			if ( w.identification != "IWAD" && w.identification != "PWAD" )
				Debug.Log( "Файл " + wadname + " не является типом IWAD или PWAD" );

			return w;
		}

		public filelump_t[] ReadLumpTable()
		{
			filelump_t[] table = new filelump_t[ wadinfo.numlumps ];

			reader.BaseStream.Seek( wadinfo.infotableofs, SeekOrigin.Begin );
			for ( int i = 0; i < wadinfo.numlumps; i++ )
			{
				//int start_pos = ( int ) reader.BaseStream.Position;

				table[ i ].filepos = reader.ReadInt32();
				table[ i ].size = reader.ReadInt32();
				table[ i ].name = ReadString( reader );

				//int end_pos = ( int ) reader.BaseStream.Position;

				//Debug.Log( "Readed lump info " + i + " at position " + start_pos + " [ position delta : " + ( end_pos - start_pos ) + " ]" );
			}

			return table;
		}

		public WadMapData ReadMapData( BinaryReader reader, string mapname )
		{
			// Ищем карту
			int mapLumpIndex = FindLumpIndexByName( mapname );
			if ( mapLumpIndex == -1 )
			{
				Debug.LogError( "Карта с именем " + mapname + " отсутсвует" );
				return null;
			}

			WadMapData map = new WadMapData();

			map.vertices = ReadMapVertices( lumptable[ mapLumpIndex + ( int ) MapLumpType.ML_VERTEXES ], reader );
			map.linedefs = ReadMapLineDefs( lumptable[ mapLumpIndex + ( int ) MapLumpType.ML_LINEDEFS ], reader );
			map.sectors = ReadMapSectors( lumptable[ mapLumpIndex + ( int ) MapLumpType.ML_SECTORS ], reader );
			map.sidedefs = ReadMapSideDefs( lumptable[ mapLumpIndex + ( int ) MapLumpType.ML_SIDEDEFS ], reader );
			map.subsectors = ReadMapSubSectors( lumptable[ mapLumpIndex + ( int ) MapLumpType.ML_SSECTORS ], reader );
			map.segs = ReadMapSegs( lumptable[ mapLumpIndex + ( int ) MapLumpType.ML_SEGS ], reader );
			map.nodes = ReadMapNodes( lumptable[ mapLumpIndex + ( int ) MapLumpType.ML_NODES ], reader );

			// Подсчитываем количество "чистых" вершин, используемых лайндефами

			map.onlyLinedefVertexCount = 0;
			foreach ( maplinedef_t ld in map.linedefs )
			{
				map.onlyLinedefVertexCount = Mathf.Max( map.onlyLinedefVertexCount, ld.v1 );
				map.onlyLinedefVertexCount = Mathf.Max( map.onlyLinedefVertexCount, ld.v2 );
			}
			map.onlyLinedefVertexCount++;

			// skyCeilingHeight

			map.skyCeilingHeight = short.MinValue;
			foreach ( mapsector_t s in map.sectors )
				if ( s.ceilingpic == skyTexName )
					map.skyCeilingHeight = Math.Max( map.skyCeilingHeight, s.ceilingheight );

			return map;
		}

		

		public int FindLumpIndexByName( string name )
		{
			for ( int i = 0; i < lumptable.Length; i++ )
				if ( string.Compare( lumptable[ i ].name, name, true ) == 0 )
					return i;
			return -1;
		}

		public int FindMapTextureIndexByName( string name )
		{
			for ( int i = 0; i < maptextures.Length; i++ )
				if ( string.Compare( maptextures[ i ].name, name, true ) == 0 )
					return i;
			return -1;
		}

		private byte[] ReadLumpData( string lumpName )
		{
			int lumpIndex = FindLumpIndexByName( lumpName );
			if ( lumpIndex == -1 )
				throw new UnityException( "Имя " + lumpName + " не найдено в lump-таблице" );
			return ReadLumpData( lumpIndex );
		}

		private byte[] ReadLumpData( int lumpIndex )
		{
			filelump_t lump = lumptable[ lumpIndex ];
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			byte[] array = new byte[ lump.size ];
			reader.Read( array, 0, lump.size );
			return array;
		}

		private string[] ReadPatchNames( string lumpName )
		{
			int lumpIndex = FindLumpIndexByName( lumpName );
			if ( lumpIndex == -1 )
				throw new UnityException( "Имя " + lumpName + " не найдено в lump-таблице" );
			return ReadPatchNames( lumpIndex );
		}

		private string[] ReadPatchNames( int lumpIndex )
		{
			filelump_t lump = lumptable[ lumpIndex ];
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			
			int numpnames = reader.ReadInt32();
			string[] pnames = new string[ numpnames ];
			for ( int i = 0; i < numpnames; i++ )
				pnames[ i ] = ReadString( reader );
			
			return pnames;
		}

		private maptexture_t[] ReadMapTextures( string lumpName )
		{
			int lumpIndex = FindLumpIndexByName( lumpName );
			if ( lumpIndex == -1 )
				return new maptexture_t[ 0 ];
			return ReadMapTextures( lumpIndex );
		}

		private maptexture_t[] ReadMapTextures( int lumpIndex )
		{
			filelump_t lump = lumptable[ lumpIndex ];
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );

			int numtextures = reader.ReadInt32();
			int[] offsets = new int[ numtextures ];
			for ( int i = 0; i < numtextures; i++ )
				offsets[ i ] = reader.ReadInt32();

			string[] pnames = this.pnames;
			maptexture_t[] textures = new maptexture_t[ numtextures ];
			for ( int i = 0; i < numtextures; i++ )
			{
				reader.BaseStream.Seek( lump.filepos + offsets[ i ], SeekOrigin.Begin );

				maptexture_t mt = new maptexture_t();
				mt.name = ReadString( reader );
				mt.masked = reader.ReadInt32() == 1;
				mt.width = reader.ReadInt16();
				mt.height = reader.ReadInt16();
				mt.columndirectory = reader.ReadInt32();
				mt.patchcount = reader.ReadInt16();
				mt.patches = new mappatch_t[ mt.patchcount ];

				for ( int j = 0; j < mt.patchcount; j++ )
				{
					mappatch_t p = new mappatch_t();
					p.originx = reader.ReadInt16();
					p.originy = reader.ReadInt16();
					p.patch = reader.ReadInt16();
					p.stepdir = reader.ReadInt16();
					p.colormap = reader.ReadInt16();

					mt.patches[ j ] = p;
				}

				textures[ i ] = mt;
			}

			return textures;
		}

		private mapvertex_t[] ReadMapVertices( filelump_t lump, BinaryReader reader )
		{
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			int num = lump.size / wad_mapvertex_size;
			mapvertex_t[] array = new mapvertex_t[ num ];
			for ( int i = 0; i < num; i++ )
				array[ i ] = ReadMapVertex( reader );
			return array;
		}

		private maplinedef_t[] ReadMapLineDefs( filelump_t lump, BinaryReader reader )
		{
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			int num = lump.size / wad_maplinedef_size;
			maplinedef_t[] array = new maplinedef_t[ num ];
			for ( int i = 0; i < num; i++ )
				array[ i ] = ReadMapLineDef( reader );
			return array;
		}

		private mapsector_t[] ReadMapSectors( filelump_t lump, BinaryReader reader )
		{
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			int num = lump.size / wad_mapsector_size;
			mapsector_t[] array = new mapsector_t[ num ];
			for ( int i = 0; i < num; i++ )
				array[ i ] = ReadMapSector( reader );
			return array;
		}

		private mapsidedef_t[] ReadMapSideDefs( filelump_t lump, BinaryReader reader )
		{
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			int num = lump.size / wad_mapsidedef_size;
			mapsidedef_t[] array = new mapsidedef_t[ num ];
			for ( int i = 0; i < num; i++ )
				array[ i ] = ReadMapSideDef( reader );
			return array;
		}

		private mapsubsector_t[] ReadMapSubSectors( filelump_t lump, BinaryReader reader )
		{
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			int num = lump.size / wad_mapsubsector_size;
			mapsubsector_t[] array = new mapsubsector_t[ num ];
			for ( int i = 0; i < num; i++ )
				array[ i ] = ReadMapSubSector( reader );
			return array;
		}

		private mapseg_t[] ReadMapSegs( filelump_t lump, BinaryReader reader )
		{
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			int num = lump.size / wad_mapseg_size;
			mapseg_t[] array = new mapseg_t[ num ];
			for ( int i = 0; i < num; i++ )
				array[ i ] = ReadMapSeg( reader );
			return array;
		}

		private mapnode_t[] ReadMapNodes( filelump_t lump, BinaryReader reader )
		{
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );
			int num = lump.size / wad_mapnode_size;
			mapnode_t[] array = new mapnode_t[ num ];
			for ( int i = 0; i < num; i++ )
				array[ i ] = ReadMapNode( reader );
			return array;
		}


		private mapvertex_t ReadMapVertex( BinaryReader reader )
		{
			mapvertex_t v = new mapvertex_t();
			v.x = reader.ReadInt16();
			v.y = reader.ReadInt16();
			return v;
		}

		private maplinedef_t ReadMapLineDef( BinaryReader reader )
		{
			maplinedef_t ld = new maplinedef_t();
			ld.v1 = reader.ReadInt16();
			ld.v2 = reader.ReadInt16();
			ld.flags = reader.ReadInt16();
			ld.special = reader.ReadInt16();
			ld.tag = reader.ReadInt16();
			ld.rsidenum = reader.ReadInt16();
			ld.lsidenum = reader.ReadInt16();
			return ld;
		}

		private mapsector_t ReadMapSector( BinaryReader reader )
		{
			mapsector_t s = new mapsector_t();
			s.floorheight = reader.ReadInt16();
			s.ceilingheight = reader.ReadInt16();
			s.floorpic = ReadString( reader );
			s.ceilingpic = ReadString( reader );
			s.lightlevel = reader.ReadInt16();
			s.special = reader.ReadInt16();
			s.tag = reader.ReadInt16();
			return s;
		}

		private mapsidedef_t ReadMapSideDef( BinaryReader reader )
		{
			mapsidedef_t s = new mapsidedef_t();
			s.textureoffset = reader.ReadInt16();
			s.rowoffset = reader.ReadInt16();
			s.toptexture = ReadString( reader );
			s.bottomtexture = ReadString( reader );
			s.midtexture = ReadString( reader );
			s.sector = reader.ReadInt16();
			return s;
		}

		private mapsubsector_t ReadMapSubSector( BinaryReader reader )
		{
			mapsubsector_t s = new mapsubsector_t();
			s.numsegs = reader.ReadInt16();
			s.firstseg = reader.ReadInt16();
			return s;
		}

		private mapseg_t ReadMapSeg( BinaryReader reader )
		{
			mapseg_t s = new mapseg_t();
			s.v1 = reader.ReadInt16();
			s.v2 = reader.ReadInt16();
			s.angle = reader.ReadInt16();
			s.linedef = reader.ReadInt16();
			s.side = reader.ReadInt16();
			s.offset = reader.ReadInt16();
			return s;
		}

		private mapnode_t ReadMapNode( BinaryReader reader )
		{
			mapnode_t n = new mapnode_t();
			n.x = reader.ReadInt16();
			n.y = reader.ReadInt16();
			n.dx = reader.ReadInt16();
			n.dy = reader.ReadInt16();
			n.rbox.yMax = reader.ReadInt16();
			n.rbox.yMin = reader.ReadInt16();
			n.rbox.xMin = reader.ReadInt16();
			n.rbox.xMax = reader.ReadInt16();
			n.lbox.yMax = reader.ReadInt16();
			n.lbox.yMin = reader.ReadInt16();
			n.lbox.xMin = reader.ReadInt16();
			n.lbox.xMax = reader.ReadInt16();
			n.rchild = reader.ReadInt16();
			n.lchild = reader.ReadInt16();
			return n;
		}

		public Texture CreateTexture_Flat( string texname, FilterMode textureFilter )
		{
			int lumpIndex = FindLumpIndexByName( texname );
			if ( lumpIndex == -1 )
			{
				Log.WriteLine( "Flat-текстура с именем [" + texname + "] не найдена" );
				Debug.LogError( "Flat-текстура с именем [" + texname + "] не найдена" );
				return null;
			}

			byte[] pixels = ReadLumpData( lumpIndex );
			return CreateTexture( wad_flat_side_size, wad_flat_side_size, false, textureFilter, pixels );
		}

		public Texture CreateTexture_Wall( string texname, FilterMode textureFilter )
		{
			int mtIndex = FindMapTextureIndexByName( texname );
			if ( mtIndex == -1 )
			{
				Log.WriteLine( "Wall-текстура с именем [" + texname + "] не найдена" );
				Debug.LogError( "Wall-текстура с именем [" + texname + "] не найдена" );
				return null;
			}

			maptexture_t mt = maptextures[ mtIndex ];
			PrintMapTexture( mt );

			// Инициализируем данные текстуры

			int channelCount = 2;
			int pixels_size = mt.width * mt.height * channelCount;
			byte[] pixels = new byte[ pixels_size ];
			for ( int i = 0; i < pixels_size; i++ )
				pixels[ i ] = 0;

			// Здесь мы рендерим патчи в одну текстуру

			for ( int i = 0; i < mt.patches.Length; i++ )
			{
				mappatch_t mp = mt.patches[ i ];
				string pname = pnames[ mp.patch ];

				Log.WriteLine( "  [Wall Patch: " + pname + "]" );
				Log.WriteLine( "    originx: " + mp.originx );
				Log.WriteLine( "    originy: " + mp.originy );
				Log.WriteLine( "    stepdir: " + mp.stepdir );
				Log.WriteLine( "    colormap: " + mp.colormap );

				// Читаем изображение патча
				patch_t p = CreateTexture_Patch( pname );

				//Log.WriteLine( "  [Real Patch: " + pname + "]" );
				Log.WriteLine( "    width: " + p.width );
				Log.WriteLine( "    height: " + p.height );
				Log.WriteLine( "    leftoffset: " + p.leftoffset );
				Log.WriteLine( "    topoffset: " + p.topoffset );

				// Рендеринг пикселей
				for ( int patchY = 0; patchY < p.height; patchY++ )
				{
					int dstRow = mp.originy + patchY;
					if ( dstRow < 0 || dstRow >= mt.height )
						continue;

					for ( int patchX = 0; patchX < p.width; patchX++ )
					{
						int dstColumn = mp.originx + patchX;
						if ( dstColumn < 0 || dstColumn >= mt.width )
							continue;

						int dstIndex = dstColumn + dstRow * mt.width;
						int srcIndex = patchX + patchY * p.width;

						int srcOffset = srcIndex * channelCount;
						int dstOffset = dstIndex * channelCount;

						// Прозрачные пропускаем
						if ( p.pixels[ srcOffset + 1 ] == 0 )
							continue;

						pixels[ dstOffset + 0 ] = p.pixels[ srcOffset + 0 ];
						pixels[ dstOffset + 1 ] = p.pixels[ srcOffset + 1 ];
					}
				}
			}

			return CreateTexture( mt.width, mt.height, true, textureFilter, pixels );
		}

		public patch_t CreateTexture_Patch( string patchName )
		{
			int lumpIndex = FindLumpIndexByName( patchName );
			if ( lumpIndex == -1 )
			{
				Log.WriteLine( "Patch-текстура с именем [" + patchName + "] не найдена" );
				Debug.LogError( "Patch-текстура с именем [" + patchName + "] не найдена" );
				return null;
			}

			filelump_t lump = lumptable[ lumpIndex ];
			reader.BaseStream.Seek( lump.filepos, SeekOrigin.Begin );

			patch_t p = new patch_t();
			p.width = reader.ReadInt16();
			p.height = reader.ReadInt16();
			p.leftoffset = reader.ReadInt16();
			p.topoffset = reader.ReadInt16();
			
			p.columnofs = new int[ p.width ];
			for ( int i = 0; i < p.width; i++ )
				p.columnofs[ i ] = reader.ReadInt32();

			int channelCount = 2;
			int pixels_size = p.width * p.height * channelCount;
			p.pixels = new byte[ pixels_size ];
			for ( int i = 0; i < pixels_size; i++ )
				p.pixels[ i ] = 0;

			for ( int column = 0; column < p.width; column++ )
			{
				reader.BaseStream.Seek( lump.filepos + p.columnofs[ column ], SeekOrigin.Begin );

				byte rowstart = 0;
				while ( rowstart < 255 )
				{
					rowstart = reader.ReadByte();
					if ( rowstart == 255 )
						break;

					byte pixel_count = reader.ReadByte();
					reader.ReadByte();
					for ( int i = 0; i < pixel_count; i++ )
					{
						byte pixel = reader.ReadByte();
						int pixelIndex = column + ( rowstart + i ) * p.width;
						int pixelOffset = pixelIndex * channelCount;
						p.pixels[ pixelOffset + 0 ] = pixel;
						p.pixels[ pixelOffset + 1 ] = 1;
					}
					reader.ReadByte();
				}
			}

			return p;
		}

		public Texture CreateTexture( int w, int h, bool transparent, FilterMode textureFilter, byte[] pixels )
		{
			Texture2D tex;
			if ( transparent )
				tex = new Texture2D( w, h, TextureFormat.RGBA32, false, false );
			else
				tex = new Texture2D( w, h, TextureFormat.RGB24, false, false );
			
			tex.filterMode = textureFilter;

			for ( int i = 0; i < h; i++ )
			{
				int y = h - i - 1;
				for ( int j = 0; j < w; j++ )
				{
					int channelCount = transparent ? 2 : 1;
					int pixelIndex = ( j + i * w );
					int pixelOffset = pixelIndex * channelCount;
					int colorIndex = pixels[ pixelOffset ];
					int colorOffset = colorIndex * wad_palette_channels;

					byte r = palette[ colorOffset + 0 ];
					byte g = palette[ colorOffset + 1 ];
					byte b = palette[ colorOffset + 2 ];
					byte a = transparent ? pixels[ pixelOffset + 1 ] == 1 ? ( byte ) 255 : ( byte ) 0 : ( byte ) 255;

					Color c = new Color(
						( float ) r / 255f,
						( float ) g / 255f,
						( float ) b / 255f,
						( float ) a / 255f );

					tex.SetPixel( j, y, c );
				}
			}
			tex.Apply();
			return tex;
		}

		public static string ReadString( BinaryReader reader, int size = wad_string_size )
		{
			return new string( reader.ReadChars( size ) ).TrimEnd( '\0' );
		}

		//----------------------------------------------------------
		// LOGGING
		//----------------------------------------------------------

		// Выводим список ламп в читабельной форме в файл
		public void PrintLumpList()
		{
			for ( int i = 0; i < lumptable.Length; i++ )
			{
				filelump_t lump = lumptable[ i ];
				Log.Write( i + ": [ " + lump.name + ", pos: " + lump.filepos + ", size: " + lump.size + " ]\n" );
			}
		}

		public void PrintMapTextures()
		{
			for ( int i = 0; i < maptextures.Length; i++ )
			{
				maptexture_t mt = maptextures[ i ];
				PrintMapTexture( mt );
			}
		}

		public void PrintMapTexture( maptexture_t mt )
		{
			Log.WriteLine( "[Wall Texture: " + mt.name + "]" );
			Log.WriteLine( "  width: " + mt.width );
			Log.WriteLine( "  height: " + mt.height );
			Log.WriteLine( "  masked: " + mt.masked );
			Log.WriteLine( "  patchcount: " + mt.patchcount );
			
			for ( int i = 0; i < mt.patches.Length; i++ )
			{
				mappatch_t mp = mt.patches[ i ];
				string pname = pnames[ mp.patch ];

				Log.WriteLine( "  [Wall Patch: " + pname + "]" );
				Log.WriteLine( "    originx: " + mp.originx );
				Log.WriteLine( "    originy: " + mp.originy );
				Log.WriteLine( "    stepdir: " + mp.stepdir );
				Log.WriteLine( "    colormap: " + mp.colormap );
			}
		}

		public void PrintPatchNames()
		{
			for ( int i = 0; i < pnames.Length; i++ )
				Log.WriteLine( string.Format( "[PNAME: {0}] {1}", i, pnames[ i ] ) );
		}

		public void LogLump( filelump_t lump )
		{
			Debug.Log( "name: " + lump.name + ", pos: " + lump.filepos + ", size: " + lump.size );
		}
	}
}